package com.xianggu.tomcat;

import com.alibaba.fastjson2.JSONObject;
import com.xianggu.spring.annotation.GetMapping;
import com.xianggu.spring.annotation.PostMapping;
import com.xianggu.spring.annotation.RequestBody;
import com.xianggu.spring.annotation.RequestParam;
import com.xianggu.spring.enums.HttpEnum;
import com.xianggu.tomcat.servlet.ErrDefaultServlet;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author chengjy
 * @date 2025/3/6
 */
public class SocketProcessor implements TomcatTaskable {

    private Socket socket;
    private Tomcat tomcat;

    public SocketProcessor(Socket socket, Tomcat tomcat) {
        this.socket = socket;
        this.tomcat = tomcat;
    }

    @Override
    public void run() {
        // 判断是哪种处理模式
        if (tomcat.isDeployed()) {
            // servlet模式
            processorSocket(socket);
        }else{
            // 控制器资源模式
            processorController(socket);
        }
    }

    private void processorController(Socket socket) {
        System.out.println(Thread.currentThread().getName() + "线程 建立连接：" + socket.toString());
        StringBuilder errMessage = new StringBuilder();
        try {
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            // 解析请求头信息
            Map<String,String> reqHeadMap =  analyRequest(inputStream);
            String headText = reqHeadMap.get("head");
            String headBody = reqHeadMap.get("body");
            String requestHead = headText.split("\r\n")[0];
            // 组装请求头
            String pathParamText = requestHead.split(" ")[1];
            String path = pathParamText;
            // 获取请求参数
            Map<String,Object> paramMap = new LinkedHashMap<>();
            if (pathParamText.contains("?")) {
                path = pathParamText.substring(pathParamText.indexOf("/",2) , pathParamText.indexOf("?"));
                String[] split = pathParamText.substring(pathParamText.indexOf("?") + 1).split("&");
                // 每段路径参数： name=1&age=2
                for (String s : split) {
                    String[] kv = s.split("=");
                    paramMap.put(kv[0], kv[1]);
                }
            }

            Request request = new Request(requestHead.split(" ")[0], pathParamText, requestHead.split(" ")[2], socket);
            System.out.println("请求报文：" + request);
            Response response = new Response(request);
            // 转发请求
            String method = requestHead.split(" ")[0];
            String url = requestHead.split(" ")[1];

            String[] urlSplit = url.split("/");
            String requestPath = urlSplit[1];


            Object bean = tomcat.getContBean(requestPath);
            Class<?> aClass = bean.getClass();
            // 需要执行的方法
            Method targetMethod = null;

            for (Method declaredMethod : aClass.getDeclaredMethods()) {
                if (HttpEnum.GET.equals(method) && declaredMethod.isAnnotationPresent(GetMapping.class)) {
                    GetMapping annotation = declaredMethod.getAnnotation(GetMapping.class);
                    String value = annotation.value();
                    if (value.equals(path)) {
                        targetMethod = declaredMethod;
                        break;
                    }
                } else if (HttpEnum.POST.equals(method) && declaredMethod.isAnnotationPresent(PostMapping.class)) {
                    PostMapping annotation = declaredMethod.getAnnotation(PostMapping.class);
                    String value = annotation.value();
                    if (value.equals(path)) {
                        targetMethod = declaredMethod;
                        break;
                    }
                }
            }
            if(targetMethod != null){
                targetMethod.setAccessible(true);
                // 返回结果
                Object invokeValue = null;

                // 判断方法有没有入参
                Parameter[] parameters = targetMethod.getParameters();
                Object[] args = new Object[parameters.length];

                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    // 路径传参
                    if (parameter.isAnnotationPresent( RequestParam.class)) {
                        RequestParam annotation = parameter.getAnnotation(RequestParam.class);
                        String paramName = annotation.value();
                        // 从解析的参数中获取
                        Object paramValue = paramMap.getOrDefault(paramName, null);
                        args[i] = paramValue;
                    }else if(parameter.isAnnotationPresent( RequestBody.class)){
                        // 请求体传参
                        Class<?> type = parameter.getType();
                        if( headBody == null){
                            errMessage.append("请求体为空！请检查 \r\n");
                        }
                        // 将json 转成 对象
                        Object object = JSONObject.parseObject(headBody, type);
                        args[i] = object;
                    } else{
                        // 没有param注解，无法获取对应的入参属性名，随机取一个返回
                        args[i] = paramMap.values().stream().findFirst().orElse(null);
                    }
                }

                if (parameters.length == 0) {
                    invokeValue = targetMethod.invoke(bean);
                }else{
                    //组装参数
                    invokeValue = targetMethod.invoke(bean , args);
                }
                response.getOutputStream().write(invokeValue.toString().getBytes(StandardCharsets.UTF_8));
            }else{
                response.sendError("资源不存在！");
            }
            response.complete();

            inputStream.close();
            outputStream.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("IO异常");
        } catch (InvocationTargetException e) {
            throw new RuntimeException("invoke方法执行失败，请检查");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, String> analyRequest(InputStream inputStream) throws IOException {
        // 使用 BufferedInputStream 包装，支持 mark/reset 复用流
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        bis.mark(Integer.MAX_VALUE);

        // 解析请求头
        Map<String, String> headers = new HashMap<>();
        StringBuilder headerBuilder = new StringBuilder();
        int b;
        while ((b = bis.read()) != -1) {
            headerBuilder.append((char) b);
            // 检查是否到达请求头结束标记 "\r\n\r\n"
            if (headerBuilder.length() >=4 &&
                    headerBuilder.substring(headerBuilder.length()-4).equals("\r\n\r\n")) {
                break;
            }
        }
        String[] headerLines = headerBuilder.toString().split("\r\n");
        for (String line : headerLines) {
            if (line.contains(":")) {
                String[] parts = line.split(":", 2);
                headers.put(parts[0].trim().toLowerCase(), parts[1].trim());
            }
        }

        // 根据 Content-Length 或 Transfer-Encoding 解析请求体
        byte[] bodyBytes;
        if (headers.containsKey("content-length")) {
            int contentLength = Integer.parseInt(headers.get("content-length"));
            bodyBytes = new byte[contentLength];
            bis.read(bodyBytes);
        } else if (headers.containsKey("transfer-encoding") &&
                headers.get("transfer-encoding").equalsIgnoreCase("chunked")) {
            // 解析分块传输编码
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            while (true) {
                String chunkSizeLine = readChunkSizeLine(bis);
                int chunkSize = Integer.parseInt(chunkSizeLine.trim(), 16);
                if (chunkSize == 0) {
                    break;
                }
                byte[] chunk = new byte[chunkSize];
                bis.read(chunk);
                buffer.write(chunk);
                bis.skip(2); // 跳过 "\r\n"
            }
            bodyBytes = buffer.toByteArray();
        } else {
            // 无 Content-Length 且非分块传输，视为无请求体
            bodyBytes = new byte[0];
        }

        // 根据 Content-Type 的 charset 解码（示例用 UTF-8）
        String charset = "UTF-8";
        if (headers.containsKey("content-type")) {
            String[] parts = headers.get("content-type").split(";");
            for (String part : parts) {
                if (part.trim().startsWith("charset=")) {
                    charset = part.split("=", 2)[1].trim();
                }
            }
        }
        String requestBody = new String(bodyBytes, charset);
        System.out.println("请求体内容: " + requestBody);

        // 重置流以便后续处理
        bis.reset();

        Map<String,String> result = new HashMap<>();
        result.put( "head",headerBuilder.toString());
        result.put( "body",requestBody);
        return result;
    }
    // 读取分块大小行
    private String readChunkSizeLine(InputStream bis) throws IOException {
        StringBuilder sb = new StringBuilder();
        int b;
        while ((b = bis.read()) != -1) {
            if (b == '\r') {
                bis.read(); // 跳过 '\n'
                break;
            }
            sb.append((char) b);
        }
        return sb.toString();
    }
    private void processorSocket(Socket socket) {
        System.out.println(Thread.currentThread().getName() + "线程 建立连接：" + socket.toString());

        try {
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            inputStream.read(bytes);
            String headText = new String(bytes);
            String requestHead = headText.split("\r\n")[0];
            // 组装请求头
            Request request = new Request(requestHead.split(" ")[0], requestHead.split(" ")[1], requestHead.split(" ")[2], socket);
            System.out.println("请求报文：" + request);
            Response response = new Response(request);
            // 转发请求
            String url = requestHead.split(" ")[1];

            String[] urlSplit = url.split("/");
            String appName = urlSplit[1];
            String path = urlSplit[2];

            Context context = tomcat.getContextMap(appName);
            Servlet servlet = context.getServlet(path);

            // 返回默认Servlet
            if(servlet == null){
                servlet = new ErrDefaultServlet();
            }
            servlet.service(request , response);
            response.complete();

            inputStream.close();
            socket.getOutputStream().close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("IO异常");
        } catch (ServletException e) {
            e.printStackTrace();
            throw new RuntimeException("请求Servlet容器异常！");
        }
    }

    @Override
    public void setTomcat(Tomcat tomcat, Socket socket) {
        this.tomcat = tomcat;
        this.socket = socket;
    }
}
